

//
#include "jni.h"
#include "errno.h"
#include "EGL/egl.h"
#include "GLES3/gl3.h"
//Android Native activity support
#include "android_native_app_glue.h"
#include "eglTest.h"
#include "GLUtils.h"

#define PX_COUNTOF(x) (sizeof(x)/sizeof(x[0]))

typedef struct _px_color
{
    union
    {
        struct
        {
            unsigned char r;
            unsigned char g;
            unsigned char b;
            unsigned char a;
        };
        unsigned int ucolor;
    }_argb;
}px_color;

struct android_app *pAndroidApp;
EGLDisplay display;
EGLSurface surface;
EGLContext context;

GLuint vertexShader;
GLuint pixelsShader;
GLuint programObject;
GLuint vPosition;
GLuint vTex;
GLuint renderTexture;
int32_t width;
int32_t height;


static const char PX_AndroidEngine_GL_VS[]="attribute vec4 vPosition;"
                                           "attribute vec4 vTex;"
                                           "varying vec2 textureCoordinate;"
                                           "void main(){"
                                           "gl_Position = vPosition;"
                                           "textureCoordinate = vTex.xy;"
                                           "}";
static const char PX_AndroidEngine_GL_PS[]="precision mediump float;"
                                           "varying highp vec2 textureCoordinate;"
                                           "uniform sampler2D tex;"
                                           "void main(){"
                                           "gl_FragColor = texture2D(tex, textureCoordinate);"
                                           "}";

static const GLfloat PX_AndroidEngine_squareVertices[] = {
        -1.0f, -1.0f,
        1.0f, -1.0f,
        -1.0f,  1.0f,
        1.0f,  1.0f,
};

static const GLfloat PX_AndroidEngine_texturePosition[] = {
        0.0f, 1.0f,
        1.0f, 1.0f,
        0.0f,  0.0f,
        1.0f,  0.0f,
};


static GLuint LoadShader(GLenum type,const char *shaderSources)
{
    GLuint shader;
    GLint compiled;

    shader=glCreateShader(type);
    if(shader==0) return  false;

    glShaderSource(shader,1,&shaderSources,NULL);
    glCompileShader(shader);

    glGetShaderiv(shader,GL_COMPILE_STATUS,&compiled);

    if(!compiled)
    {
        glDeleteShader(shader);
        return false;
    }
    return  shader;
}

bool  InitializeDisplay()
{
    const int attrib_list[] = { EGL_CONTEXT_CLIENT_VERSION, 2, EGL_NONE };
    const EGLint attribs[] = {
            EGL_SURFACE_TYPE, EGL_WINDOW_BIT,
            EGL_BLUE_SIZE, 8,
            EGL_GREEN_SIZE, 8,
            EGL_RED_SIZE, 8,
            EGL_NONE
    };
    EGLint w, h, format;
    EGLint numConfigs;
    EGLConfig config;
    EGLConfig ConfigsDesc[64];
    int i;
    GLint linked;
    display = eglGetDisplay(EGL_DEFAULT_DISPLAY);



    eglInitialize(display, 0, 0);
    eglChooseConfig(display, attribs, NULL,0, &numConfigs);
    if(numConfigs>PX_COUNTOF(ConfigsDesc))
    {
        numConfigs=PX_COUNTOF(ConfigsDesc);
    }
    eglChooseConfig(display, attribs,ConfigsDesc, numConfigs, &numConfigs);

    for (i=0; i < numConfigs; i++)
    {
        EGLint r, g, b, d, a;
        if (eglGetConfigAttrib(display, ConfigsDesc[i], EGL_RED_SIZE, &r)   &&
            eglGetConfigAttrib(display, ConfigsDesc[i], EGL_GREEN_SIZE, &g) &&
            eglGetConfigAttrib(display, ConfigsDesc[i], EGL_BLUE_SIZE, &b)  &&
            eglGetConfigAttrib(display, ConfigsDesc[i], EGL_ALPHA_SIZE, &a)&&
            eglGetConfigAttrib(display, ConfigsDesc[i], EGL_DEPTH_SIZE, &d) &&
            r == 8 && g == 8 && b == 8 &&a == 8&&d==0 )
        {
            config = ConfigsDesc[i];
            break;
        }
    }

    if (i == numConfigs) {
        config = ConfigsDesc[0];
    }

    /* EGL_NATIVE_VISUAL_ID is an attribute of the EGLConfig that is
     * guaranteed to be accepted by ANativeWindow_setBuffersGeometry().
     * As soon as we picked a EGLConfig, we can safely reconfigure the
     * ANativeWindow buffers to match, using EGL_NATIVE_VISUAL_ID. */
    eglGetConfigAttrib(display, config, EGL_NATIVE_VISUAL_ID, &format);
    surface = eglCreateWindowSurface(display, config, pAndroidApp->window, NULL);
    context = eglCreateContext(display, config, EGL_NO_CONTEXT, attrib_list);

    if (eglMakeCurrent(display, surface, surface, context) == EGL_FALSE)
    {
        return false;
    }

    eglQuerySurface(display, surface, EGL_WIDTH, &w);
    eglQuerySurface(display, surface, EGL_HEIGHT, &h);

    display = display;
    context = context;
    surface = surface;
    width = w;
    height = h;

    // Initialize GL state.
    glEnable(GL_CULL_FACE);
    glEnable(GL_TEXTURE_2D);
    glEnable(GL_TEXTURE0);
    glEnable(GL_TEXTURE1);
    glDisable(GL_DEPTH_TEST);

    //Initialize GL shader
    vertexShader=LoadShader(GL_VERTEX_SHADER,PX_AndroidEngine_GL_VS);
    if(vertexShader==0)
        return  false;
    pixelsShader=LoadShader(GL_FRAGMENT_SHADER,PX_AndroidEngine_GL_PS);
    if(pixelsShader==0)
        return false;

    programObject=glCreateProgram();
    if(programObject==0)
        return false;

    glAttachShader(programObject,vertexShader);
    glAttachShader(programObject,pixelsShader);

    glLinkProgram(programObject);

    glGetProgramiv(programObject,GL_LINK_STATUS,&linked);
    if(!linked)
    {
        glDeleteProgram(programObject);
        return false;
    }

    glGenTextures(1,&renderTexture);

    vPosition=glGetAttribLocation(programObject,"vPosition");
    vTex=glGetAttribLocation(programObject,"vTex");

    glViewport(0,0,width,height);


    return true;
}

bool  FreeDisplay()
{
    if (display != EGL_NO_DISPLAY) {
        eglMakeCurrent(display, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT);
        if (context != EGL_NO_CONTEXT) {
            eglDestroyContext(display, context);
        }
        if (surface != EGL_NO_SURFACE) {
            eglDestroySurface(display, surface);
        }
        eglTerminate(display);
    }

    display = EGL_NO_DISPLAY;
    context = EGL_NO_CONTEXT;
    surface = EGL_NO_SURFACE;

    glDetachShader(programObject,vertexShader);
    glDetachShader(programObject,pixelsShader);
    glDeleteShader(pixelsShader);
    glDeleteShader(vertexShader);
    glDeleteProgram(programObject);

    return  true;
}

static Test::eglTest* peglTest=nullptr;

void Engine_Render(unsigned char* buffer) {
    if (display == NULL)
    {
        // No display.
        return;
    }

    //vertex data
//    glClearColor(1.0f,0.0f,0.0f,1.0f);
//    glClear (GL_DEPTH_BUFFER_BIT | GL_COLOR_BUFFER_BIT);

    glUseProgram(programObject);

    glActiveTexture(GL_TEXTURE0);

    /////////////////////////
    GLUtils::CheckGLError("start Engine_Render");
    GLuint tmpt=GLUtils::createColorfulTexture(width, height);
    uint32_t *outbuf=GLUtils::createTmpPictureEmpty(width,height);
//    glBindFramebuffer(GL_FRAMEBUFFER, tmpt);
//    glFramebufferTexture2D(GL_READ_FRAMEBUFFER, GL_COLOR_ATTACHMENT0,GL_TEXTURE_2D, tmpt, 0);
    //
//    GLuint pix_buf;
//    glGenBuffers(1, &pix_buf);
//    glBindBuffer(GL_PIXEL_PACK_BUFFER, pix_buf);
//    glBufferData(GL_PIXEL_PACK_BUFFER, width*height*sizeof(uint32_t), NULL, GL_STREAM_READ);
//    glBindBuffer(GL_PIXEL_PACK_BUFFER, 0);
//
//    glBindBuffer(GL_PIXEL_PACK_BUFFER, pix_buf);
//    glReadPixels( 0,  0, width, height, GL_RGBA, GL_UNSIGNED_BYTE, 0);
//
//    glBindBuffer(GL_PIXEL_PACK_BUFFER, pix_buf);
//    uint32_t *tmp = (uint32_t*)glMapBufferRange(GL_PIXEL_PACK_BUFFER, 0, width*height*sizeof(uint32_t), GL_MAP_READ_BIT);
//    memcpy(outbuf,tmp,width*height*sizeof(uint32_t));
//    glUnmapBuffer(GL_PIXEL_PACK_BUFFER);
//    glBindBuffer(GL_PIXEL_PACK_BUFFER, 0);
//

    //

    //读取纹理的像素
    GLuint fbo;
    glGenFramebuffers(1,&fbo);
    glBindFramebuffer(GL_READ_FRAMEBUFFER, fbo);
    glFramebufferTexture2D(GL_READ_FRAMEBUFFER, GL_COLOR_ATTACHMENT0,GL_TEXTURE_2D, tmpt, 0);
    glReadPixels(0, 0, width, height, GL_RGBA, GL_UNSIGNED_BYTE,outbuf);
    glBindTexture(GL_TEXTURE_2D, 0);
    glBindFramebuffer(GL_READ_FRAMEBUFFER, 0);

    //////////////////////////////////////
    glActiveTexture(GL_TEXTURE0);
    glBindTexture(GL_TEXTURE_2D,renderTexture);

    glTexImage2D(GL_TEXTURE_2D,0,GL_RGBA,width,height,0,GL_RGBA,GL_UNSIGNED_BYTE,outbuf);
    glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MIN_FILTER,GL_LINEAR);
    glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MAG_FILTER,GL_LINEAR);
    glUniform1i(glGetAttribLocation(programObject,"tex"), 0);

    glVertexAttribPointer(vPosition, 2, GL_FLOAT, GL_FALSE, 0,PX_AndroidEngine_squareVertices);
    glEnableVertexAttribArray(vPosition);
    glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);

    glVertexAttribPointer(vTex, 2, GL_FLOAT, 0, 0, PX_AndroidEngine_texturePosition);
    glEnableVertexAttribArray(vTex);
    GLUtils::CheckGLError("start Engine_Render 1");
//    if(peglTest==nullptr){
//        peglTest=new Test::eglTest();
//
//    }
//    peglTest->egltest();


    ////////////////////////////////
//
//    GLuint tmpt=GLUtils::createTexture(width, height,0xffff00ff);
//////
////    GLuint targetFBO;
////    glGenFramebuffers(GL_FRAMEBUFFER, &targetFBO);
////    glBindFramebuffer(GL_FRAMEBUFFER, 0);
//    glActiveTexture(GL_TEXTURE0);
//    glBindTexture(GL_TEXTURE_2D, tmpt);
//
//    uint32_t *outbuf=GLUtils::createTmpPicture(width,height);
//    glReadPixels(0, 0, width, height, GL_RGBA, GL_UNSIGNED_BYTE,outbuf);
//        glBindFramebuffer(GL_FRAMEBUFFER, 0);
//        glBindTexture(GL_TEXTURE_2D, 0);

    GLUtils::CheckGLError("glReadPixels");

//        GLuint fbo;
//        glGenFramebuffers(1,&fbo);
//        glBindFramebuffer(GL_READ_FRAMEBUFFER, fbo);
//        glFramebufferTexture2D(GL_READ_FRAMEBUFFER, GL_COLOR_ATTACHMENT0,GL_TEXTURE_2D, src_id, 0);
//        glBindTexture(GL_TEXTURE_2D, dest_id);
//        glCopyTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, 0, 0, width,height);
//            glGetTexImage(GL_TEXTURE_2D,0,GL_RGBA,GL_UNSIGNED_BYTE,&outputValues[0]);

//    memcpy(outBuffer.bits,outbuf,width*height*sizeof(uint32_t));

//    GLUtils::savePicture("output",width,height,outbuf,FUNC_LINE);

//    glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D,tmpt , 0);
//////
//    glActiveTexture(GL_TEXTURE0);
//    glBindTexture(GL_TEXTURE_2D, tmpt);
////    glCopyTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, 0, 0, width, height, 0);
//    glCopyTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0,0, 0, width, height);


    eglSwapBuffers(display, surface);
}



static int PX_AndroidEngine_Input(struct android_app* app, AInputEvent* event) {

    int EventType=AInputEvent_getType(event);
    int EventCount=AMotionEvent_getPointerCount(event);
    int action=AMotionEvent_getAction(event);
    float cursorx_scale,cursory_scale;
    int i;
    float x,y;

    switch (action&AMOTION_EVENT_ACTION_MASK)
    {
        case AMOTION_EVENT_ACTION_MOVE:
        {
            for (i = 0; i < EventCount; ++i)
            {
                x=AMotionEvent_getX(event, i);
                y=AMotionEvent_getY(event, i);

            }
        }
            break;
        case AMOTION_EVENT_ACTION_POINTER_DOWN:
        case AMOTION_EVENT_ACTION_DOWN:
        {
            for (i = 0; i < EventCount; ++i)
            {
                x=AMotionEvent_getX(event, i);
                y=AMotionEvent_getY(event, i);

            }
        }
            break;
        case AMOTION_EVENT_ACTION_POINTER_UP:
        case AMOTION_EVENT_ACTION_UP:
        {
            for (i = 0; i < EventCount; ++i)
            {
                x=AMotionEvent_getX(event, i);
                y=AMotionEvent_getY(event, i);

            }
        }
            break;
    }

    return 0;
}

void PX_AndroidEngine_CMD(struct android_app* app, int32_t cmd)
{
    switch (cmd)
    {
        case APP_CMD_SAVE_STATE:
            break;
        case APP_CMD_INIT_WINDOW:
            if (pAndroidApp->window != NULL)
            {
                //PainterEngine Initialize
                InitializeDisplay();
            }


            break;
        case APP_CMD_TERM_WINDOW:
            //FreeDisplay();
            break;
        case APP_CMD_GAINED_FOCUS:
            break;
        case APP_CMD_LOST_FOCUS:
            break;
    }
}


ANativeActivity* PX_nativeActivity=NULL;
AAssetManager* PX_assetManager=NULL;
#include<stdlib.h>
void android_main(struct android_app *pApp)
{

    unsigned int lastUpdateTime,elpased;
    int events;
    struct android_poll_source *pSource;
    pApp->userData=NULL;
    pApp->onAppCmd = PX_AndroidEngine_CMD;
    pApp->onInputEvent = PX_AndroidEngine_Input;
    pAndroidApp=pApp;
    PX_nativeActivity = pApp->activity;
    PX_assetManager = PX_nativeActivity->assetManager;
    do
    {
        while (ALooper_pollAll(0, NULL, &events, (void **) &pSource) >= 0)
        {
            if (pSource)
            {
                pSource->process(pApp, pSource);
                if (pApp->destroyRequested != 0) {
                    FreeDisplay();
                    return;
                }
            }
        }

        unsigned int  *mysurface=(unsigned int  *)malloc(height*width*sizeof(unsigned int ));;
        for(int i=0;i<height;i++)
            for(int j=0;j<width;j++){
                mysurface[j*width+i]=i%255;
            }

        Engine_Render((unsigned char*)mysurface);
        free(mysurface);

    } while (1);
}
